#include <string>

#include "binary_patterns.h"
#include "grate/util/common.h"
#include "grate/util/word.h"

namespace growth_rate {
namespace binary_patterns {

bool is_xy_pattern(const Word &word, const std::string &pattern,
                   size_t xlen, size_t ylen) {
  CHECK_GT(xlen, 0);
  CHECK_GT(ylen, 0);
  // Count the expected length.
  size_t exp_length = 0;
  for (size_t i = 0; i < pattern.length(); i++) {
    CHECK(pattern[i] == 'x' || pattern[i] == 'y');
    exp_length += (pattern[i] == 'x') ? xlen : ylen;
  }
  if (word.length() != exp_length) return false;
  Word X, Y;
  size_t cur_index = 0;
  for (size_t i = 0; i < pattern.length(); i++) {
    if (pattern[i] == 'x') {
      if (X.length() == 0) {
        X = word.substr(cur_index, xlen);
      } else if (X != word.substr(cur_index, xlen)) {
        return false;
      }
      cur_index += xlen;
    } else {
      DCHECK_EQ('y', pattern[i]);
      if (Y.length() == 0) {
        Y = word.substr(cur_index, ylen);
      } else if (Y != word.substr(cur_index, ylen)) {
        return false;
      }
      cur_index += ylen;
    }
  }
  // We've read the whole word.
  CHECK_EQ(word.length(), cur_index);
  return true;
}

Word build_xy_pattern(uint8_t *buffer, const std::string &pattern,
                      const Word &X, const Word &Y) {
  CHECK_GT(X.length(), 0);
  CHECK_GT(Y.length(), 0);
  Word word = Word::createWordFromArray(buffer, 0);
  for (size_t i = 0; i < pattern.length(); i++) {
    CHECK(pattern[i] == 'x' || pattern[i] == 'y');
    word = (pattern[i] == 'x') ? word.append(X) : word.append(Y);
  }
  return word;
}

static size_t guess_queue_capacity() {
  // Use 1/4 of all available memory for queue.
  return static_cast<size_t>(PhysicalMemory() / 4);
}

static size_t guess_trie_capacity() {
  // Use 1/4 of all avaialbe memory for trie.
  size_t mem = static_cast<size_t>(PhysicalMemory() / 4);
  // Estimate memory needed for each node: at least DFA transition table
  // and counters for eigenvalue calculation.
  size_t mem_per_node = 2 * sizeof(int) + 2 * sizeof(double);  // NOLINT
  return mem / mem_per_node;
}

LexminAntiDictionary *get_antidictionary_for_pattern(
    const std::string &pattern, size_t root_length,
    size_t queue_capacity, size_t trie_capacity) {
  if (queue_capacity == 0)
    queue_capacity = guess_queue_capacity();
  if (trie_capacity == 0)
    trie_capacity = guess_trie_capacity();

  BinaryPatternAD::Options options;
  options.pattern = pattern;
  options.max_root_length = root_length;
  options.queue_capacity = queue_capacity;
  options.trie_capacity = trie_capacity;

  if (pattern == "xyxyxx") {
    return new Pattern_XYXYXX(options);
  } else if (pattern == "xyxxyxy") {
    return new Pattern_4X_3Y(options);
  } else if (pattern == "xyxyyxx") {
    return new Pattern_4X_3Y(options);
  } else if (pattern == "xyxxyxy,xxx") {
    options.pattern = "xyxxyxy";
    return new Pattern_4X_3Y(options, true);
  } else if (pattern == "xyxyyxx,xxx") {
    options.pattern = "xyxyyxx";
    return new Pattern_4X_3Y(options, true);
  } else if (pattern == "xyxxyyxy") {
    return new Pattern_4X_4Y(options);
  } else if (pattern == "xyxxyyxy,xxx") {
    options.pattern = "xyxxyyxy";
    return new Pattern_4X_4Y(options, true);
  } else {
    Report("unsupported binary pattern: %s\n", pattern.c_str());
    UNIMPLEMENTED();
    return 0;
  }
}

}  // namespace binary_patterns
}  // namespace growth_rate

